package com.lsa.helpers.graph;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.lsa.helpers.IConverter;

public class TgfGraphFactory
{
    private static String nodeSectionSplitRegex = "(.+)\\s+(.*)";
    private static Pattern nodeSectionSlitPattern = Pattern.compile(nodeSectionSplitRegex);
    
    private static String edgeSectionSplitRegex = "\\s";
    
    private static String sectionRegex = ".*[#].*";
    private static Pattern sectionPattern = Pattern.compile(sectionRegex);
    
    private static String lineSeparator = System.getProperty("line.separator");
    
    public static <T, U> IGraph<T, U> readGraphFromFile(String path, IConverter<String, T> nodeDataConverter, IConverter<String, U> edgeDataConverter, boolean isDirected ) throws IOException{
        BufferedReader reader = new BufferedReader(new FileReader(new File(path)));
        String line = null;
        
        boolean isEdgesSection = false;
        
        IGraph<T, U> graph = new AdjGraph<T, U>(isDirected, 0);
        Map<String, Integer> id2NodeIndex = new HashMap<String, Integer>();
        
        int currentNodeIndex = 0;
        
        while(( line = reader.readLine()) != null){
            if (sectionPattern.matcher(line).matches()){
                isEdgesSection = true;
                continue;
            }
            else{
                if (!isEdgesSection){
                    Matcher matcher = nodeSectionSlitPattern.matcher(line);
                    if (matcher.find()){
                        String id = matcher.group(1);
                        String label = matcher.groupCount() == 2? matcher.group(2) : id;
                        graph.addNode(nodeDataConverter.convert(label));
                        id2NodeIndex.put(id, currentNodeIndex++);
                    }    
                }
                else{
                    final String[] parts = line.split(edgeSectionSplitRegex);
                    if (parts.length > 1) {
                        String id1 = parts[0];
                        String id2 = parts[1];
                        U edgeData = null;
                        if (parts.length > 2) {
                            edgeData = edgeDataConverter.convert(parts[2]);
                        }
                        graph.addEdge(id2NodeIndex.get(id1), id2NodeIndex.get(id2), edgeData);
                    }
                }
            }
        }
        
        return graph;
    }
    
    public static void saveGraphAsTgf(IGraph<String, String> graph, String path) throws IOException{
    	saveGraphAsTgf(graph, new DefaultToStringConverter(), new DefaultToStringConverter(), path);
    }
    
    public static <T, U> void saveGraphAsTgf(IGraph<T, U> graph, IConverter<T, String> nodeDataConverter, IConverter<U, String> edgeDataConverter,  String path) throws IOException{
        final BufferedWriter writer = new BufferedWriter(new FileWriter(path));

        Iterator<T> vertexIter = graph.vertexIterator();
        int nodeIndex = 0;
        while(vertexIter.hasNext()){
        	T data = vertexIter.next();
        	writer.write((nodeIndex++) + " " + nodeDataConverter.convert(data) + lineSeparator);
        }

        writer.write("#" + lineSeparator);
        
        Iterator<IEdgeNode<U>> edgeIter = graph.edgeIterator();
        while(edgeIter.hasNext()){
        	IEdgeNode<U> edgeNode = edgeIter.next();
        	writer.write(edgeNode.from() + " " + edgeNode.to() + " " + edgeDataConverter.convert(edgeNode.getData()) + lineSeparator);
        }
        
        writer.close();
    }
}
